namespace DependencyLib
{
	using System;
	using System.Collections.Generic;
	using System.Text;
	using CrossFlow.GraphLib;
	using System.Xml;
	using System.IO;

  public class DigraphAsVisioDrawingWriter
  {
    private XmlDocument drawing;
    private XmlDocument vertex;
    private XmlDocument edge;
    private XmlDocument connect;
    private int shape_count;
    private XmlNamespaceManager names;

    public DigraphAsVisioDrawingWriter()
    {
      Stream stream = this.GetType().Assembly.GetManifestResourceStream("DependencyLib.template.visioDrawing.xml");
      using (StreamReader reader = new StreamReader(stream))
      {
        drawing = new XmlDocument();
        drawing.Load(reader);
      }
      stream = this.GetType().Assembly.GetManifestResourceStream("DependencyLib.template.shapeVertex.xml");
      using (StreamReader reader = new StreamReader(stream))
      {
        vertex = new XmlDocument();
        vertex.Load(reader);
      }
      stream = this.GetType().Assembly.GetManifestResourceStream("DependencyLib.template.shapeEdge.xml");
      using (StreamReader reader = new StreamReader(stream))
      {
        edge = new XmlDocument();
        edge.Load(reader);
      }
      stream = this.GetType().Assembly.GetManifestResourceStream("DependencyLib.template.connect.xml");
      using (StreamReader reader = new StreamReader(stream))
      {
        connect = new XmlDocument();
        connect.Load(reader);
      }
      shape_count = 1;

      names = new XmlNamespaceManager(this.drawing.NameTable);
      names.AddNamespace("co", "http://schemas.microsoft.com/visio/2003/core");
    }

    public void AddVertex(Vertex vertex)
    {
      XmlDocumentFragment newvertex = this.drawing.CreateDocumentFragment();
      newvertex.InnerXml = this.vertex.DocumentElement.OuterXml;

      XmlNode shapes = this.drawing.SelectSingleNode("/co:VisioDocument/co:Pages/co:Page/co:Shapes", this.names);
      shapes.AppendChild(newvertex);

      XmlNode shape = shapes.SelectSingleNode("co:Shape[@ID='0']", names);
      shape.Attributes["ID"].Value = shape_count.ToString();
      vertex.Data = shape_count;
      ++shape_count;
      shape["Text"].InnerText = vertex.Name;
    }

    public void AddEdge(Edge edge)
    {
      XmlDocumentFragment newedge = this.drawing.CreateDocumentFragment();
      newedge.InnerXml = this.edge.DocumentElement.OuterXml;

      XmlNode shapes = this.drawing.SelectSingleNode("/co:VisioDocument/co:Pages/co:Page/co:Shapes", this.names);
      shapes.AppendChild(newedge);

      XmlNode shape = shapes.SelectSingleNode("co:Shape[@ID='0']", names);
      shape.Attributes["ID"].Value = shape_count.ToString();
      edge.Data = shape_count;
      ++shape_count;
    }

    public void ConnectVertexes(Edge edge)
    {
      XmlDocumentFragment leftlink = this.drawing.CreateDocumentFragment();
      leftlink.InnerXml = this.connect.DocumentElement.OuterXml;

      XmlNode connects = this.drawing.SelectSingleNode("/co:VisioDocument/co:Pages/co:Page/co:Connects", this.names);
      connects.AppendChild(leftlink);

      XmlNode leftconnect = connects.SelectSingleNode("co:Connect[@FromSheet='X']", this.names);
      leftconnect.Attributes["FromSheet"].Value = edge.Data.ToString();
      leftconnect.Attributes["ToSheet"].Value = edge.Source.Data.ToString();
      leftconnect.Attributes.RemoveNamedItem("xmlns");
      leftconnect.Attributes.RemoveNamedItem("xmlns:vx");

      XmlDocumentFragment rightlink = this.drawing.CreateDocumentFragment();
      rightlink.InnerXml = this.connect.DocumentElement.OuterXml;
      connects.AppendChild(rightlink);

      XmlNode rightconnect = connects.SelectSingleNode("co:Connect[@FromSheet='X']", this.names);
      rightconnect.Attributes["FromSheet"].Value = edge.Data.ToString();
      rightconnect.Attributes["ToSheet"].Value = edge.Target.Data.ToString();
      rightconnect.Attributes["FromCell"].Value = "EndX";
      rightconnect.Attributes.RemoveNamedItem("xmlns");
      rightconnect.Attributes.RemoveNamedItem("xmlns:vx");
    }

    public void Save(TextWriter writer)
    {
      this.drawing.Save(writer);
    }

    public void Write(TextWriter writer, DirectedGraph graph)
    {
      foreach (Vertex vertex in graph.Vertexes)
        AddVertex(vertex);
      foreach (Edge edge in graph.Edges)
        AddEdge(edge);
      foreach (Edge edge in graph.Edges)
        ConnectVertexes(edge);
      Save(writer);
    }

    public void Append(string current_drawing_xml, GraphDelta delta, TextWriter new_drawing_writer)
    {
      this.drawing = new XmlDocument();
      this.drawing.LoadXml(current_drawing_xml);

      foreach (Vertex vertex in delta.Vertexes)
        AddVertex(vertex);
      foreach (Edge edge in delta.Edges)
        AddEdge(edge);
      foreach (Edge edge in delta.Edges)
        ConnectVertexes(edge);

      Save(new_drawing_writer);
    }
  }
}